﻿'
' Copyright (c) 2010-2012, MatthiWare
'
' Permission is hereby granted, free of charge, to any person obtaining a
' copy of this software and associated documentation files (the "Software"),
' to deal in the Software without restriction, including without limitation
' the rights to use, copy, modify, merge, publish, distribute, sublicense,
' and/or sell copies of the Software, and to permit persons to whom the
' Software is furnished to do so, subject to the following conditions:
'
' The above copyright notice and this permission notice shall be included in
' all copies or substantial portions of the Software. 
' You shall include 'MatthiWare' in the credits/about section of the program
'
' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
' IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
' FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
' AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
' LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
' FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
' DEALINGS IN THE SOFTWARE.
'

Imports SharpDX
Imports SharpDX.Direct2D1
Imports TerrariaVB.Core.Base
Imports TerrariaVB.Core.Helper
Imports System.Threading
Imports System.Runtime.Serialization.Formatters.Binary

<Serializable()>
Public Class World
    Implements IDisposable

    Public Shared WorldWidth As Double = 160
    Public Shared WorldHeight As Double = 160

    Property Players As New List(Of Player)

    Public Shared Updater As BlockUpdater

    Public level(WorldWidth, WorldHeight) As Block

    Public Shared PixelSize As Int32 = 2

    Public ItemCollection As List(Of WorldItem)

    Public Shared MaxOreSize As Int32 = 5

    Public Npcs As New List(Of Npc)

    'Public Sub Save()
    '    Dim wrld As New World()
    '    Dim xml As New BinaryFormatter()
    '    xml.Serialize(New IO.FileStream(".\wrld1.dat", IO.FileMode.OpenOrCreate), level)
    '    MessageBox.Show("Done")
    'End Sub

    'Public Shared Sub Load()

    'End Sub

    Public Function AddPlayer(p As Player) As Boolean
        Dim pc() As Player = Players.ToArray()
        Dim canCreate As Boolean = True

        For Each pi As Player In pc
            If (pi.Name = p.Name) Then
                canCreate = False
                Exit For
            End If
        Next

        If (canCreate) Then
            Players.Add(p)
        End If

        Return canCreate
    End Function

    Public Sub New()
        Updater = New BlockUpdater()
        ItemCollection = New List(Of WorldItem)
    End Sub

    Public Sub Tick(e As Core.Base.GameCoreEventArgs)
        DoWorldActions(e)
        Dim p As Player = e.Core.Services.GetService(GetType(Player))
        Dim c As Camera = p.Cam
        Dim npclist() As Npc = Npcs.ToArray()
        For Each n As Npc In npclist
            If (n.IsAlive) Then
                n.Update(e)
            Else
                Npcs.Remove(n)
                n.Dispose()
            End If
        Next
        Dim ic() As WorldItem = ItemCollection.ToArray()
        For Each i As WorldItem In ic
            'Dim xx As Int32 = (i.X / Tile.TileSize)
            'Dim yy As Int32 = (i.Y / Tile.TileSize)

            'If (xx > c.X And xx < c.X + Camera.Vision.Width And yy > c.Y And yy < c.Y + Camera.Vision.Height) Then
            i.Tick(e)
            'End If
        Next
    End Sub

    Private Sub DoWorldActions(e As GameCoreEventArgs)
        DestroyBlock(e)
        BuildBlock(e)
    End Sub

    Public Sub SpawnItem(x As Double, y As Double, t As Integer, e As GameCoreEventArgs)
        Dim ii As Item = Item.LoadItem(t, e)
        Dim i As New WorldItem(x, y)
        i.Item = ii

        ItemCollection.Add(i)
    End Sub

    Public Sub DestroyBlock(e As GameCoreEventArgs)
        Dim p As Player = e.Core.Services.GetService(GetType(Player))
        If (p.leftMouseDown) Then
            If (p.inventory.selectedSlot.Info.Item.IsPickaxe) Then
                If (p.mouseX >= 0 And p.mouseX < World.WorldWidth And p.mouseY >= 0 And p.mouseY < World.WorldHeight) Then
                    ' coord. is goed
                    Dim world As World = e.Core.Services.GetService(GetType(World))
                    Dim b As Block = world.level(p.mouseX, p.mouseY)
                    If (Not IsNothing(b)) Then
                        If (Not b.Info.Name = "Air") Then

                            If Utils.Contains(p.MineRange, b.rect) Then

                                b.timeBeingMined += 1

                                If (b.timeBeingMined > b.miningTime) Then
                                    Dim spawnId As Int32 = 0

                                    Select Case (b.Info.Name)
                                        Case "Dirt"
                                            spawnId = 1
                                        Case "Grass"
                                            spawnId = 1
                                        Case "Stone"
                                            spawnId = 3
                                        Case "Copper Ore"
                                            spawnId = 6
                                        Case "Iron Ore"
                                            spawnId = 7
                                        Case "Silver Ore"
                                            spawnId = 8
                                        Case "Gold Ore"
                                            spawnId = 9
                                        Case Else
                                            spawnId = 1
                                    End Select

                                    SpawnItem(p.mouseX, p.mouseY, spawnId, e)

                                    b.Info = Tile.Air

                                    world.Updater.DoThreadedBlockUpdate(b, e)
                                End If
                            End If
                        End If
                    End If
                End If
            End If
        End If
    End Sub

    Public Sub BuildBlock(e As GameCoreEventArgs)
        Dim world As World = e.Core.Services.GetService(GetType(World))
        Dim p As Player = e.Core.Services.GetService(GetType(Player))
        Dim i As Inventory = p.inventory
        If (p.rightMouseDown) Then
            If (p.inventory.selectedSlot.Info.Item.IsPlacable) Then
                If (p.mouseX >= 0 And p.mouseX < World.WorldWidth And p.mouseY >= 0 And p.mouseY < World.WorldHeight) Then
                    ' coord. is goed
                    Dim b As Block = world.level(p.mouseX, p.mouseY)
                    If (Not IsNothing(b)) Then
                        If (b.Info.Name = "Air") Then
                            If Utils.Contains(p.MineRange, b.rect) Then
                                Dim s As InventorySlot = i.selectedSlot

                                Select Case (s.Info.Item.Name)
                                    Case "Dirt"
                                        b.Info = Tile.Dirt
                                    Case "Stone"
                                        b.Info = Tile.Stone
                                    Case Else

                                End Select

                                s.Info.Stack -= 1
                                If (s.Info.Stack = 0) Then
                                    s.Info = New InvSlotInfo(e)
                                End If
                                world.Updater.DoThreadedBlockUpdate(b, e)
                            End If
                        End If
                    End If
                End If
            End If
        End If
    End Sub

    Public Sub AddCopper(seed As Int32)
        Dim max As Integer = Math.Floor((WorldWidth * WorldHeight) / 1500)
        Dim rand As New Random()

        For intCount As Int32 = 0 To Math.Floor((WorldWidth * WorldHeight) / 1500)
            Dim intX As Int32 = rand.Next(1, WorldWidth - 1)
            Dim intY As Int32 = rand.Next(((WorldHeight / 9) * 2), WorldHeight - 1)
            Dim intWidth As Int32 = rand.Next(1, 6)
            Dim intHeight As Int32 = rand.Next(1, 6)

            For x As Int32 = intX To intX + intWidth
                For y As Int32 = intY To intY + intHeight

                    If (x < WorldWidth And y < WorldHeight) Then
                        level(x, y).Info = Tile.Copper

                        level(rand.Next(-1, 1) + x, y).Info = Tile.Copper
                        level(x, rand.Next(-1, 1) + y).Info = Tile.Copper
                    End If


                Next
            Next
        Next
    End Sub
    Public Sub AddIron(seed As Int32)
        Dim max As Integer = Math.Floor((WorldWidth * WorldHeight) / 2000)
        Dim rand As New Random()

        For intCount As Int32 = 0 To Math.Floor((WorldWidth * WorldHeight) / 2000)
            Dim intX As Int32 = rand.Next(1, WorldWidth - 1)
            Dim intY As Int32 = rand.Next(((WorldHeight / 9) * 3), WorldHeight - 1)
            Dim intWidth As Int32 = rand.Next(1, 6)
            Dim intHeight As Int32 = rand.Next(1, 6)

            For x As Int32 = intX To intX + intWidth
                For y As Int32 = intY To intY + intHeight

                    If (x < WorldWidth And y < WorldHeight) Then
                        level(x, y).Info = Tile.Iron

                        level(rand.Next(-1, 1) + x, y).Info = Tile.Iron
                        level(x, rand.Next(-1, 1) + y).Info = Tile.Iron
                    End If


                Next
            Next
        Next
    End Sub
    Public Sub AddSilver(seed As Int32)
        Dim max As Integer = Math.Floor((WorldWidth * WorldHeight) / 2500)
        Dim rand As New Random()

        For intCount As Int32 = 0 To Math.Floor((WorldWidth * WorldHeight) / 2500)
            Dim intX As Int32 = rand.Next(1, WorldWidth - 1)
            Dim intY As Int32 = rand.Next(((WorldHeight / 9) * 4), WorldHeight - 1)
            Dim intWidth As Int32 = rand.Next(1, 6)
            Dim intHeight As Int32 = rand.Next(1, 6)

            For x As Int32 = intX To intX + intWidth
                For y As Int32 = intY To intY + intHeight

                    If (x < WorldWidth And y < WorldHeight) Then
                        level(x, y).Info = Tile.Silver

                        level(rand.Next(-1, 1) + x, y).Info = Tile.Silver
                        level(x, rand.Next(-1, 1) + y).Info = Tile.Silver
                    End If


                Next
            Next
        Next
    End Sub
    Public Sub AddGold(seed As Int32)
        Dim max As Integer = Math.Floor((WorldWidth * WorldHeight) / 3000)
        Dim rand As New Random()

        For intCount As Int32 = 0 To Math.Floor((WorldWidth * WorldHeight) / 3000)
            Dim intX As Int32 = rand.Next(1, WorldWidth - 1)
            Dim intY As Int32 = rand.Next(((WorldHeight / 9) * 6), WorldHeight - 1)
            Dim intWidth As Int32 = rand.Next(1, 6)
            Dim intHeight As Int32 = rand.Next(1, 6)

            For x As Int32 = intX To intX + intWidth
                For y As Int32 = intY To intY + intHeight

                    If (x < WorldWidth And y < WorldHeight) Then
                        level(x, y).Info = Tile.Gold


                        level(rand.Next(-1, 1) + x, y).Info = Tile.Gold
                        level(x, rand.Next(-1, 1) + y).Info = Tile.Gold
                    End If


                Next
            Next
        Next
    End Sub

    Public Sub AddOres(seed As Int32)
        AddCopper(seed)
        AddIron(seed)
        AddSilver(seed)
        AddGold(seed)
    End Sub

    Public Sub AddStone(seed As Int32)
        Dim max As Int32 = Math.Floor((WorldWidth * WorldHeight) / 300)
        Dim rand As New Random()
        For intCount As Int32 = 0 To Math.Floor((WorldWidth * WorldHeight) / 300)

            Dim intX As Int32 = rand.Next(1, WorldWidth - 1)
            Dim intY As Int32 = rand.Next(21, WorldHeight - 1)
            Dim intWidth As Int32 = rand.Next(1, 4)
            Dim intHeight As Int32 = rand.Next(1, 4)

            For x As Int32 = intX To intX + intWidth
                For y As Int32 = intY To intY + intHeight

                    If (x < WorldWidth And y < WorldHeight) Then
                        level(x, y).Info = Tile.Stone
                    End If


                Next
            Next
        Next
    End Sub

    Public Sub AddCaves(seed As Int32)
        Dim max As Int32 = Math.Floor((WorldHeight * WorldWidth) / 1000)
        Dim rand As New Random()
        For intCount As Int32 = 0 To Math.Floor((WorldHeight * WorldHeight) / 1000)

            Dim intX As Int32 = rand.Next(1, WorldWidth - 1)
            Dim intY As Int32 = rand.Next(21, WorldHeight - 1)
            Dim intWidth As Int32 = rand.Next(3, 20)
            Dim intHeight As Int32 = rand.Next(3, 10)

            For x As Int32 = intX To intX + intWidth
                For y As Int32 = intY To intY + intHeight

                    If (x < WorldWidth And y < WorldHeight) Then
                        level(x, y).Info = Tile.Air

                        level(rand.Next(-1, 1) + x, y).Info = Tile.Air
                        level(x, rand.Next(-1, 1) + y).Info = Tile.Air

                    End If


                Next
            Next
        Next
    End Sub

    Public Sub SpawnNpc(x As Int32, y As Int32, id As Int32, e As GameCoreEventArgs)
        Dim npc As New Npc(x, y, id, e)
        Npcs.Add(npc)
    End Sub

    Protected Friend Sub GenerateWorldDebug(e As GameCoreEventArgs)
        Dim max As Int32 = (WorldWidth - 1)
        Dim intOres As Integer = 0
        Dim intp As Int32 = 0

        For x As Int32 = 0 To WorldWidth - 1
            For y As Int32 = 0 To WorldHeight - 1

                level(x, y) = New Block(New Point(x, y), New Size(Tile.TileSize, Tile.TileSize), Tile.Dirt)

                If (y < 20) Then
                    level(x, y).Info = Tile.Air
                ElseIf (y = 20) Then
                    level(x, y).Info = Tile.Grass
                Else
                    level(x, y).Info = Tile.Dirt
                End If
            Next
        Next

        AddStone(1995)
        AddOres(1995)
        AddCaves(1995)
        For x As Int32 = (WorldWidth / 2) + (WorldWidth / 4) To (WorldWidth / 2) + (WorldWidth / 4) + 4
            For y As Int32 = 0 To WorldHeight / 2
                level(x, y).Info = Tile.Air
            Next
        Next

        For x As Int32 = 0 To WorldWidth - 1
            For y As Int32 = 0 To WorldHeight - 1
                World.Updater.RequestBlockCheck(level(x, y), e)
            Next
        Next
    End Sub

    Public Sub GenerateWorld(seed As Int32, e As GameCoreEventArgs)

        Dim random As New Random(seed)

        For x As Int32 = 0 To WorldWidth - 1

            ' Bergen..
            Dim intY As Integer = random.Next(0, WorldHeight - 1)

            Do Until (intY < random.Next(5, WorldHeight - 5))
                intY -= 1
            Loop

            For y As Int32 = 0 To WorldHeight - 1

                level(x, y) = New Block(New Point(x, y), New Size(Tile.TileSize, Tile.TileSize), Tile.Dirt)

                If (y < intY) Then
                    level(x, y).Info = Tile.Air
                ElseIf (y = intY) Then
                    level(x, y).Info = Tile.Grass
                Else
                    level(x, y).Info = Tile.Dirt
                End If


            Next
        Next
        For x As Int32 = 0 To WorldWidth - 1
            For y As Int32 = 0 To WorldHeight - 1
                World.Updater.RequestBlockCheck(level(x, y), e)
            Next
        Next
    End Sub

    Public Sub Render(e As Core.Base.DrawEventArgs)
        Dim p As Player = e.Core.Services.GetService(GetType(Player))
        Dim c As Camera = p.Cam

        If (p.Underground) Then

        Else

        End If

        e.Target.DrawBitmap(Tile.Sky, New DrawingRectangleF(0, 0, CInt((Camera.Vision.Width + 5) * Tile.TileSize), CInt((Camera.Vision.Height + 20) * Tile.TileSize)), 1.0F, BitmapInterpolationMode.Linear)

        For x As Int32 = c.X - 1 To Camera.Vision.Width + c.X
            For y As Int32 = c.Y - 1 To Camera.Vision.Height + c.Y
                ' Make sure we are not rendering outside the world bounds
                If (x > -1 And x < WorldWidth And y > -1 And y < WorldHeight) Then
                    level(x, y).Draw(e)
                End If
            Next
        Next

        Dim ic() As WorldItem = ItemCollection.ToArray()

        For Each i As WorldItem In ic
            'Dim xx As Int32 = (i.X / Tile.TileSize)
            'Dim yy As Int32 = (i.Y / Tile.TileSize)
            'If (xx > c.X And xx < c.X + Camera.Vision.Width And yy > c.Y And yy < c.Y + Camera.Vision.Height) Then
            Dim b As Bitmap = Item.ItemImageCollection(i.Item.Type)
            e.Target.DrawBitmap(b, i.X, i.Y)
            'End If
        Next

        Dim npclist() As Npc = Npcs.ToArray()
        For Each n As Npc In npclist
            If (n.X > c.X And n.X < c.X + Camera.Vision.Width And n.Y > c.Y And n.Y < c.Y + Camera.Vision.Height) Then
                n.Draw(e)
            End If
        Next
    End Sub

#Region "IDisposable Support"
    Private disposedValue As Boolean ' To detect redundant calls

    ' IDisposable
    Protected Overridable Sub Dispose(disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                ' TODO: dispose managed state (managed objects).
                For Each b As Block In level
                    If (Not IsNothing(b)) Then
                        b.Dispose()
                        b = Nothing
                    End If
                Next
            End If

            ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
            ' TODO: set large fields to null.
        End If
        Me.disposedValue = True
    End Sub

    ' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
    Protected Overrides Sub Finalize()
        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        Dispose(False)
        MyBase.Finalize()
    End Sub

    ' This code added by Visual Basic to correctly implement the disposable pattern.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region

End Class
